package com.cuixichang.minimalism.core.security.intercept;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.cuixichang.minimalism.core.security.design.RuleDefinitionDesign;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    private static final Log logfactory = LogFactory.get();
    @Autowired
    private RuleDefinitionDesign ruleDefinitionDesign;
    @Autowired
    private RequestMappingHandlerMapping handlerMapping;
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) {
        HttpServletRequest httpServletRequest = ((FilterInvocation) object).getHttpRequest();
        if(logfactory.isDebugEnabled()){
            logfactory.debug("request url:{}",httpServletRequest.getRequestURI());
        }
        HandlerExecutionChain handler = matchingHandler(httpServletRequest);
        List<ConfigAttribute> configAttributes = new ArrayList<>();
        if(Objects.isNull(handler)) return configAttributes;
        HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler();
        Class<?> beanType = handlerMethod.getBeanType();
        RequestMapping annotation = beanType.getAnnotation(RequestMapping.class);
        List<String> controllerPaths = Objects.nonNull(annotation)? Arrays.asList(annotation.value()): Arrays.asList(Strings.EMPTY);
        RequestMapping methodAnnotation = handlerMethod.getMethodAnnotation(RequestMapping.class);
        List<String> methodPaths = Arrays.asList(methodAnnotation.value());
        List<String> relativePaths = controllerPaths.stream().flatMap(controllerPath -> methodPaths.stream()
                .map(methodPath -> controllerPath.concat(methodPath))).collect(Collectors.toList());
        relativePaths.forEach(path->{
            List<ConfigAttribute> routeMatchs = ruleDefinitionDesign.configAttributes(httpServletRequest, path);
            if (!CollectionUtils.isEmpty(routeMatchs)) {
                configAttributes.addAll(routeMatchs);
            }
        });
        return configAttributes;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return false;
    }

    private HandlerExecutionChain matchingHandler(HttpServletRequest httpServletRequest){
        try {
            return handlerMapping.getHandler(httpServletRequest);
        }catch (Exception e){
            return null;
        }
    }
}
